package org.visionlibrary.image.filters.thresholding;

import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.model.AbstractFilter;
import org.visionlibrary.image.model.FilterTools;


public class EntropyThreshold extends AbstractFilter {
	@Override
	public TiledImage applyFilter(TiledImage src, TiledImage dest) {
		if (null == src)
			throw new NullPointerException("Source image is null.");

		if (null == dest)
			dest = (new ImageFactory()).createCompatibleImage(src);

		int maxX = src.getWidth();
		int maxY = src.getHeight();

		int threshold = 0;

		double[] probability = new double[256];
		double[] entropy = new double[256];
		double sumOfProbBk = 0.0; // sum of probability before k
		double sumOfProbAk = 0.0; // sum of probability after k
		double sumOfProbLnBk = 0.0; // sum of probability multiplied by
									// logarithm of probability before k
		double sumOfProbLnAk = 0.0; // sum of probability multiplied by
									// logarithm of probability after k

		for (int ch = 0; ch < src.getNumBands(); ch++) {
			int[] histogram = FilterTools.getHistogram(src, ch);

			double sum = 0.0;
			for (int i = 0; i < 256; i++)
				sum += ((double) histogram[i]);

			// for(int i = 0; i< 256; i++)
			// probability[i] = ((double) histogram[i]) / (maxX * maxY);
			// //zamiast tak zsumowac histogram i liczyc jako unormowanie

			for (int i = 0; i < 256; i++)
				probability[i] = ((double) histogram[i]) / sum; // zamiast tak
																// zsumowac
																// histogram i
																// liczyc jako
																// unormowanie

			// System.out.println("probability: "
			// +java.util.Arrays.toString(probability));

			for (int k = 0; k < 256; k++) {
				sumOfProbBk = 0.0;
				sumOfProbAk = 0.0;
				sumOfProbLnBk = 0.0;
				sumOfProbLnAk = 0.0;

				for (int i = 0; i < k; i++) {
					sumOfProbBk += probability[i];

					if (probability[i] != 0.0) {
						sumOfProbLnBk += (probability[i] * Math
								.log(probability[i]));
					}
				}

				for (int i = k; i < 256; i++) {
					sumOfProbAk += probability[i];

					if (probability[i] != 0.0) {
						sumOfProbLnAk += (probability[i] * Math
								.log(probability[i]));
					}
				}

				double diffBk = 0.0;
				if (((int) sumOfProbBk) != 0)
					diffBk = (sumOfProbLnBk / sumOfProbBk);
				else
					sumOfProbBk = 1.0;

				double diffAk = 0.0;
				if (((int) sumOfProbAk) != 0)
					diffAk = (sumOfProbLnAk / sumOfProbAk);
				else
					sumOfProbAk = 1.0;

				entropy[k] = -Math.log(sumOfProbBk) - Math.log(sumOfProbAk)
						+ diffBk + diffAk;

				if (entropy[k] < 0.0) {
					entropy[k] *= -1.0;
				}
			}

			System.out
					.println("entropy: " + java.util.Arrays.toString(entropy));

			for (int i = 0; i < 256; i++) {
				if (entropy[i] > entropy[threshold]) {
					threshold = i;
				}
			}

			System.out.println("threshold: " + threshold);

			for (int x = 0; x < maxX; x++)
				for (int y = 0; y < maxY; y++) {
					int newPixelVal = src.getSample(x, y, ch);
					if (newPixelVal > threshold) {
						dest.setSample(x, y, ch, 255);
					} else {
						dest.setSample(x, y, ch, 0);
					}

				}
		}

		return dest;
	}
}
